﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using Meridian.Helpers;
using VkApi;

namespace Meridian.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        private readonly Random random;
        private readonly DispatcherTimer durationTimer;
        private VkProfile userProfile;

        private ObservableCollection<VkAudio> myAudio;
        public ObservableCollection<VkAudio> MyAudio
        {
            get { return myAudio; }
            set
            {
                if (myAudio == value)
                    return;
                myAudio = value;
                MyAudioSelectedIndex = -1;
                RaisePropertyChanged("MyAudio");
            }
        }

        private ObservableCollection<VkAudio> searchResults;
        public ObservableCollection<VkAudio> SearchResults
        {
            get { return searchResults; }
            set
            {
                if (searchResults == value)
                    return;
                searchResults = value;
                SearchSelectedIndex = -1;
                RaisePropertyChanged("SearchResults");
            }
        }

        private bool isLoading;
        public bool IsLoading
        {
            get { return isLoading; }
            set
            {
                if (isLoading == value)
                    return;
                isLoading = value;
                RaisePropertyChanged("IsLoading");
            }
        }

        private bool isLoaded;
        public bool IsLoaded
        {
            get { return isLoaded; }
            set
            {
                if (isLoaded == value)
                    return;
                isLoaded = value;
                RaisePropertyChanged("IsLoaded");
            }
        }

        private Uri userPicture;
        public Uri UserPicture
        {
            get { return userPicture; }
            set
            {
                if (userPicture == value)
                    return;
                userPicture = value;
                RaisePropertyChanged("UserPicture");
            }
        }

        private string userName;
        public string UserName
        {
            get { return userName; }
            set
            {
                if (userName == value)
                    return;
                userName = value;
                RaisePropertyChanged("UserName");
            }
        }

        private string userLastName;
        public string UserLastName
        {
            get { return userLastName; }
            set
            {
                if (userLastName == value)
                    return;
                userLastName = value;
                RaisePropertyChanged("UserLastName");
            }
        }

        private VkAudio currentAudio;
        public VkAudio CurrentAudio
        {
            get { return currentAudio; }
            set
            {
                if (currentAudio == value)
                    return;
                currentAudio = value;
                CanLike = true;
                Title = value.Title + " - " + value.Artist;
                RaisePropertyChanged("CurrentAudio");
            }
        }

        private bool isPlaying;
        public bool IsPlaying
        {
            get { return isPlaying; }
            set
            {
                if (isPlaying == value)
                    return;
                isPlaying = value;
                RaisePropertyChanged("IsPlaying");
            }
        }

        private bool isPlayingFromSearch;
        public bool IsPlayingFromSearch
        {
            get { return isPlayingFromSearch; }
            set
            {
                if (isPlayingFromSearch == value)
                    return;
                isPlayingFromSearch = value;
                CanLike = true;
                RaisePropertyChanged("IsPlayingFromSearch");
            }
        }

        private int myAudioSelectedIndex = -1;
        public int MyAudioSelectedIndex
        {
            get { return myAudioSelectedIndex; }
            set
            {
                if (myAudioSelectedIndex == value)
                    return;
                myAudioSelectedIndex = value;
                RaisePropertyChanged("MyAudioSelectedIndex");
            }
        }

        private int searchSelectedIndex = -1;
        public int SearchSelectedIndex
        {
            get { return searchSelectedIndex; }
            set
            {
                if (searchSelectedIndex == value)
                    return;
                searchSelectedIndex = value;
                RaisePropertyChanged("SearchSelectedIndex");
            }
        }

        private bool shuffle;
        public bool Shuffle
        {
            get { return shuffle; }
            set
            {
                if (shuffle == value)
                    return;
                shuffle = value;
                RaisePropertyChanged("Shuffle");
            }
        }

        private TimeSpan duration = TimeSpan.FromMilliseconds(1);
        public TimeSpan Duration
        {
            get { return duration; }
            set
            {
                if (duration == value)
                    return;
                duration = value;
                RaisePropertyChanged("Duration");
            }
        }

        private TimeSpan timeLeft;
        public TimeSpan TimeLeft
        {
            get { return timeLeft; }
            set
            {
                if (timeLeft == value)
                    return;
                timeLeft = value;
                RaisePropertyChanged("TimeLeft");
            }
        }

        private TimeSpan position;
        public TimeSpan Position
        {
            get { return App.Player.Position; }
            set
            {
                if ((int)position.TotalSeconds == (int)value.TotalSeconds)
                    return;
                position = value;
                App.Player.Position = position;
                RaisePropertyChanged("Position");
            }
        }

        private int volume;
        public int Volume
        {
            get { return volume; }
            set
            {
                if (volume == value)
                    return;
                volume = value;
                App.Player.Volume = value / 100.0f;
                RaisePropertyChanged("Volume");
            }
        }

        private bool canLike;
        public bool CanLike
        {
            get { return canLike; }
            set
            {
                if (canLike == value)
                    return;
                canLike = value;
                RaisePropertyChanged("CanLike");
            }
        }

        private string status;
        public string Status
        {
            get { return status; }
            set
            {
                if (status == value)
                    return;
                status = value;
                RaisePropertyChanged("Status");
            }
        }

        private string title = "Meridian";
        public string Title
        {
            get { return title; }
            set
            {
                if (title == value)
                    return;
                title = value;
                RaisePropertyChanged("Title");
            }
        }

        public MainViewModel()
        {
            //userPicture = new Uri("/Resources/Images/userpic_default.png", UriKind.Relative);
            UserName = Properties.Resources.SignIn;
            random = new Random(Environment.TickCount);

            durationTimer = new DispatcherTimer();
            durationTimer = new DispatcherTimer();
            durationTimer.Interval = TimeSpan.FromMilliseconds(100);
            durationTimer.Tick += DurationTimerTick;
        }

        #region VKApi methods
        public void Login()
        {
            if (!Global.IsAuthorized)
            {
                IsLoading = true;
                UserName = Properties.Resources.Authorization;
                Status = Properties.Resources.Authorization;
                //ThreadHelper.InvokeAsync(() => AuthHelper.Authorize(AuthorizeCallback));
                AuthHelper.Authorize(AuthorizeCallback);
            }
            else
                Load(App.Settings.FirstLoadItemsCount);
        }

        void AuthorizeCallback()
        {
            if (!App.Vkontakte.AuthResult.IsSuccess)
            {
                MessageBox.Show(App.Vkontakte.AuthResult.Error);
                return;
            }
            Status = string.Empty;
            Load(App.Settings.FirstLoadItemsCount);
        }

        private int errorLoginCount = 0;
        public void Load(int count)
        {
            IsLoading = true;
            ThreadHelper.InvokeAsync(() =>
                                         {
                                             var profile = App.Vkontakte.GetUserProfile();
                                             Status = Properties.Resources.StatusProfile;
                                             if (profile == null)
                                             {
                                                 Global.IsAuthorized = false;
                                                 if (App.Vkontakte.LastError != null && App.Vkontakte.LastError.ErrorCode == 5)
                                                 {
                                                     errorLoginCount++;
                                                     if (errorLoginCount < 5)
                                                     {
                                                         Login();
                                                         Load(count);
                                                     }
                                                 }
                                                 return;
                                             }

                                             errorLoginCount = 0;

                                             GetUserProfileCallback(profile);
                                             Status = Properties.Resources.StatusAudio;
                                             var audio = App.Vkontakte.GetAudio(count);

                                             GetAudioCallback(audio);
                                             Status = string.Empty;
                                             //GetUnreadMessagesCount();
                                         });
        }

        public void LoadMore(int count)
        {
            if (myAudio == null)
                return;
            if (IsLoading)
                return;
            IsLoading = true;
            ThreadHelper.InvokeAsync(() =>
                                         {
                                             Status = Properties.Resources.StatusAudio;
                                             var audio = App.Vkontakte.GetAudio(count, myAudio.Count);
                                             GetMoreAudioCallback(audio);
                                             Status = string.Empty;
                                         });

        }

        private string lastSearch;
        public void Search(string query, int count)
        {
            IsLoading = true;
            lastSearch = query;
            ThreadHelper.InvokeAsync(() =>
                                         {
                                             Status = Properties.Resources.StatusAudio;
                                             string artist = string.Empty;
                                             if (query.EndsWith(":"))
                                             {
                                                 artist = query.Substring(0, query.IndexOf(':'));
                                                 query = query.Substring(0, query.Length - 2);
                                             }
                                             var audio = App.Vkontakte.SearchAudio(query, count, artist);
                                             SearchCallback(audio);
                                             Status = string.Empty;
                                         });
        }

        public void SearchMany(string[] queries)
        {
            IsLoading = true;
            lastSearch = string.Empty;
            ThreadHelper.InvokeAsync(() =>
            {
                if (SearchResults == null)
                    SearchResults = new ObservableCollection<VkAudio>();
                else
                    Application.Current.Dispatcher.Invoke((Action)(() => SearchResults.Clear()));

                Status = Properties.Resources.StatusAudio;
                foreach (var query in queries)
                {
                    var audio = App.Vkontakte.SearchAudio(query.Trim(), 1);
                    if (audio != null && audio.Count > 0)
                    {
                        Application.Current.Dispatcher.Invoke((Action)(() => SearchResults.Add(audio[0])));
                    }
                }
                Status = string.Empty;
                IsLoading = false;
            });
        }

        public void SearchMore(int count)
        {
            if (string.IsNullOrEmpty(lastSearch))
                return;
            if (searchResults == null)
                return;
            if (IsLoading)
                return;

            IsLoading = true;
            Status = Properties.Resources.StatusAudio;
            ThreadHelper.InvokeAsync(() =>
            {
                string artist = string.Empty;
                var query = lastSearch;
                if (query.EndsWith(":"))
                {
                    artist = query.Substring(0, query.IndexOf(':'));
                    query = query.Substring(0, query.Length - 2);
                }

                var audio = App.Vkontakte.SearchAudio(query, count, searchResults.Count, artist);
                SearchMoreCallback(audio);
                Status = string.Empty;
            });
        }

        public void AddAudio(VkAudio audio)
        {
            Status = Properties.Resources.StatusAudioAdd;
            ThreadHelper.InvokeAsync(() =>
            {
                App.Vkontakte.AddAudio(audio.Id.ToString(), audio.OwnerId.ToString());
                Load(myAudio.Count);
            });
        }

        public void DeleteAudio(VkAudio audio)
        {
            Status = Properties.Resources.StatusAudioDelete;
            ThreadHelper.InvokeAsync(() =>
            {
                App.Vkontakte.DeleteAudio(audio.Id.ToString(), audio.OwnerId.ToString());
                Load(myAudio.Count);
            });
        }

        public void GetUnreadMessagesCount()
        {
            if (!Global.IsAuthorized)
                return;
            IsLoading = true;
            ThreadHelper.InvokeAsync(() =>
                                         {
                                             IsLoading = false;

                                             var unreadCount = App.Vkontakte.GetUnreadMessagesCount();
                                             if (unreadCount > 0)
                                             {
                                                 UserLastName = Properties.Resources.UnreadCount + " " + unreadCount;
                                             }
                                             else
                                             {
                                                 if (userProfile != null)
                                                     UserLastName = userProfile.LastName;
                                                 else
                                                     UserLastName = string.Empty;
                                             }
                                         });
        }

        #endregion

        public void PlayAudio(VkAudio audio)
        {
            if (audio == null)
                return;
            CurrentAudio = audio;

            if (IsPlayingFromSearch)
            {
                SearchSelectedIndex = searchResults.IndexOf(currentAudio);
                MyAudioSelectedIndex = -1;
            }
            else
            {
                MyAudioSelectedIndex = myAudio.IndexOf(currentAudio);
                SearchSelectedIndex = -1;
            }

            App.Player.Source = new Uri(audio.Url);
            IsPlaying = false;
            PlayPause();
        }

        public void PlayPause()
        {
            if (IsPlaying)
            {
                App.Player.Pause();
                durationTimer.Start();
            }
            else
            {
                if (currentAudio == null && myAudio.Count > 0)
                {
                    PlayAudio(myAudio[0]);
                    return;
                }

                App.Player.Play();
                durationTimer.Start();
            }

            IsPlaying = !IsPlaying;
        }

        public void NextAudio()
        {
            ObservableCollection<VkAudio> source = IsPlayingFromSearch ? searchResults : myAudio;

            if (source.Count == 0)
                return;

            if (currentAudio == null)
            {
                PlayAudio(source[0]);
                return;
            }

            int index;

            if (shuffle)
                index = random.Next(source.Count);
            else
                index = source.IndexOf(currentAudio) + 1;

            if (index >= source.Count)
                index = 0;

            PlayAudio(source[index]);
        }


        public void PrevAudio()
        {
            ObservableCollection<VkAudio> source = IsPlayingFromSearch ? searchResults : myAudio;

            if (source.Count == 0)
                return;

            if (currentAudio == null)
            {
                PlayAudio(source[source.Count - 1]);
                return;
            }

            int index;

            if (shuffle)
                index = random.Next(source.Count);
            else
                index = source.IndexOf(currentAudio) - 1;

            if (index < 0)
                index = source.Count - 1;

            PlayAudio(source[index]);
        }

        public void LikeDislike()
        {
            if (currentAudio.IsAddedByUser)
                DeleteAudio(currentAudio);
            else
                AddAudio(currentAudio);

            CanLike = false;
        }

        void DurationTimerTick(object sender, EventArgs e)
        {
            if (App.Player.IsBuffering)
            {
                Status = Properties.Resources.StatusBuffering + " " + App.Player.BufferingProgress * 100 + "%";
            }

            if (App.Player.NaturalDuration.HasTimeSpan)
            {
                Duration = App.Player.NaturalDuration.TimeSpan;
                TimeLeft = Position - Duration;

                if (App.Player.NaturalDuration.TimeSpan == Position)
                {
                    IsPlaying = false;
                    NextAudio();
                }
            }

            Position = App.Player.Position;
        }

        #region VKApi callbacks
        void GetUserProfileCallback(VkProfile profile)
        {
            IsLoading = false;
            UserName = profile.FirstName;
            UserLastName = profile.LastName;
            UserPicture = new Uri(profile.PhotoUrl);
            userProfile = profile;
        }

        void GetAudioCallback(List<VkAudio> audio)
        {
            IsLoading = false;
            IsLoaded = true;

            if (myAudio != null)
            {
                Application.Current.Dispatcher.Invoke((Action)(() => myAudio.Clear()));
            }
            MyAudio = new ObservableCollection<VkAudio>(audio);
        }

        void GetMoreAudioCallback(IEnumerable<VkAudio> audio)
        {
            Application.Current.Dispatcher.Invoke((Action)delegate
            {
                foreach (var vkAudio in audio)
                {
                    MyAudio.Add(vkAudio);
                }
            });
            IsLoading = false;
        }

        void SearchCallback(List<VkAudio> audio)
        {
            IsLoading = false;
            IsLoaded = true;

            if (searchResults != null)
            {
                Application.Current.Dispatcher.Invoke((Action)(() => searchResults.Clear()));
            }
            SearchResults = new ObservableCollection<VkAudio>(audio);
        }

        void SearchMoreCallback(IEnumerable<VkAudio> audio)
        {
            IsLoading = false;

            Application.Current.Dispatcher.Invoke((Action)delegate
            {
                foreach (var vkAudio in audio)
                {
                    SearchResults.Add(vkAudio);
                }
            });
        }

        #endregion

        #region Updater

        public void CheckForUpdates(bool forceCheck = false)
        {
            if (!forceCheck)
            {
#if DEBUG
                const int maxHoursForCheck = 4;
#else
                const int maxHoursForCheck = 12;
#endif
                if ((DateTime.Now - App.Settings.LastCheck).TotalHours < maxHoursForCheck)
                {
                    return;
                }
            }

            App.ViewModel.Status = Properties.Resources.StatusCheckUpdates;
            ThreadHelper.InvokeAsync(() =>
            {
                var path = Updater.CheckUpdates();
                App.Settings.LastCheck = DateTime.Now;
                if (string.IsNullOrEmpty(path))
                {
                    Status = string.Empty;
                    return;
                }

                Status = Properties.Resources.StatusDownloadingUpdates;

                if (Updater.Update(path))
                {
                    Status = Properties.Resources.StatusUpdated;
                    App.Settings.Clean = true;
                    return;
                }

                Status = string.Empty;

            });
        }

        #endregion

        #region LastFm
        
        public void SearchForAlbum(string query)
        {
            IsLoading = true;
            lastSearch = string.Empty;
            ThreadHelper.InvokeAsync(() =>
            {
                if (SearchResults == null)
                    SearchResults = new ObservableCollection<VkAudio>();
                else
                    Application.Current.Dispatcher.Invoke((Action)(() => SearchResults.Clear()));

                Status = Properties.Resources.StatusAudio;
                var tracks = App.LastFmHelper.SearchForAlbum(query);
                if (tracks != null)
                {
                    foreach (var track in tracks)
                    {
                        var audio = App.Vkontakte.SearchAudio(track.Artist + " - " + track.Title, 1);
                        if (audio != null && audio.Count > 0)
                        {
                            Application.Current.Dispatcher.Invoke((Action)(() => SearchResults.Add(audio[0])));
                        }
                    }
                }

                Status = string.Empty;
                IsLoading = false;
            });
        }

        #endregion
    }
}
